<?php

namespace App\Util;

use Illuminate\Support\Facades\Log;
use OSS\OssClient;

/**
 * Http 请求类
 */
class OSSUtil
{
    protected $config;
    protected static $ins;

    public function __construct()
    {
        $this->config = config('alipay.oss');
    }

    /**
     * 单例模式
     */
    public static function getInstance()
    {
        if (self::$ins instanceof self) {
            return self::$ins;
        }
        self::$ins = new self();
        return self::$ins;
    }

    /**
     * 获取客户端实例
     *
     * @return OssClient
     * @throws \OSS\Core\OssException
     * @author Chris Yu <chrisyu@crabapple.top> 2023/9/21
     */
    public function getClientIns()
    {
        $accessKeyId = $this->config['accessKeyId'];
        $accessKeySecret = $this->config['accessKeySecret'];
        $endpoint = $this->config['endpoint'];
        return new OssClient($accessKeyId, $accessKeySecret, $endpoint);
    }

    public function getConfig()
    {
        return $this->config;
    }

    public function setConfig($config)
    {
        $this->config = array_merge($this->config, $config);
    }

    /**
     * 获取默认 server
     * @author Chris Yu <chrisyu@crabapple.top> 2023/9/21
     */
    public function getDefaultServer()
    {
        return isset($this->config['fileUrl']) ? rtrim($this->config['fileUrl'], '/') : '';
    }

    /**
     * 获取默认桶
     * @author Chris Yu <chrisyu@crabapple.top> 2023/9/21
     */
    public function getDefaultBucket()
    {
        return $this->config['bucket'];
    }

    public function getDefaultObject($ext, $prefix = '')
    {
        $year = date('Y', time());
        $month = date('m', time());
        $day = date('d', time());
        $time = date('His', time());
        return ($prefix ? $prefix . '/' : '') . "{$year}/{$month}/{$day}/" . $time . '_' . uniqid() . '.' . $ext;
    }

    public function generateObject($ext, $prefix = '', $mode = 'default')
    {
        if ($mode === 'default') {
            return $this->getDefaultObject($ext, $prefix);
        }
        elseif ($mode === 'uniqid') {
            $year = date('Y', time());
            $month = date('m', time());
            $day = date('d', time());
            return ($prefix ? $prefix . '/' : '') . "{$year}/{$month}/{$day}/" . uniqid() . '.' . $ext;
        }
    }

    /**
     * 文件上传
     * @param $file \Illuminate\Http\UploadedFile
     * @author Chris Yu <chrisyu@crabapple.top> 2023/11/8
     */
    public function uploadFile($file, $objectPrefix = '', $bucket = '', $options = [])
    {
        $tmpPath = $file->getRealPath(); // 临时目录
        $ext = $file->getClientOriginalExtension();
        $type = $file->getType();
        $mimeType = $file->getClientMimeType();

        $ossClient = $this->getClientIns();
        $bucket = $bucket ?: $this->getDefaultBucket();
        $object = $this->generateObject($ext, $objectPrefix);

        $result = $ossClient->uploadFile($bucket, $object, $tmpPath, $options);
        if (!$result) {
            Log::error('文件上传失败', ['location' => __METHOD__ . '[' . __LINE__ . ']', 'file' => $file->getFileInfo()]);
            return false;
        }
        // 访问地址
        $ossRequestUrl = $result['oss-request-url'];
        $urlInfo = parse_url($ossRequestUrl);
        $result['access'] = [
            'server' => $this->getDefaultServer() ?: $urlInfo['schema'] . '://' . $urlInfo['host'],
            'path' => $urlInfo['path'],
            'fullUrl' => $this->getDefaultServer() . $urlInfo['path'],
        ];

        return $result;
    }

    /**
     * 上传文件。通过本地文件
     * @param $file
     * @param $objectPrefix
     * @param $bucket
     * @param $options
     *
     * @return array|false
     * @throws \OSS\Core\OssException
     * @author Chris Yu <chrisyu@crabapple.top> 2023/11/21
     */
    public function uploadFileFromLocal($filePath, $objectPrefix = '', $bucket = '', $options = [])
    {
        $ossClient = $this->getClientIns();
        $bucket = $bucket ?: $this->getDefaultBucket();
        $pathinfo = pathinfo($filePath);
        $ext = $pathinfo['extension'];
        $object = $this->generateObject($ext, $objectPrefix);

        $result = $ossClient->uploadFile($bucket, $object, $filePath, $options);
        if (!$result) {
            Log::error('文件上传失败', ['location' => __METHOD__ . '[' . __LINE__ . ']']);
            return false;
        }
        $result = $this->appendInfo($result);
        Log::info("文件上传：".json_encode($result));
        return $result;
    }

    /**
     * 上传文件。通过 url 地址
     * @param $fileUrl string 文件
     * @param $objectPrefix string 保存的对象前缀
     * @param $ext string 保存的文件后缀名
     * @param $fileInfo array 文件信息
     *
     * @return false
     * @throws \OSS\Core\OssException
     * @author Chris Yu <chrisyu@crabapple.top> 2023/11/7
     */
    public function uploadFileByFileUrl($fileUrl, $objectPrefix = '', $ext = '', &$fileInfo = [])
    {
        // 生成临时存储文件
        $ext = $ext ?: $this->getFileExtByFileUrlViaUrl($fileUrl);
        if (!$ext) {
            return false;
        }
        $tmpSavePath = storage_path('temp/' . uniqid() . '.' . $ext);
        FileUtil::createFolders(dirname($tmpSavePath));
        file_put_contents($tmpSavePath, file_get_contents($fileUrl));

        $fileInfo = pathinfo($tmpSavePath);
        $sha1 = sha1_file($tmpSavePath);
        $fileSize = filesize($tmpSavePath);;
        $ext = strtolower($fileInfo['extension']);
        $fileInfo['ext'] = $ext;
        $fileInfo['sha1'] = $sha1;
        $fileInfo['fileSize'] = $fileSize;
        $isImage = in_array($ext, ['jpg', 'jpeg', 'png']);
        if ($isImage) {
            $imageSize = getimagesize($tmpSavePath);
            $fileInfo['imageWidth'] = $imageSize[0];
            $fileInfo['imageHeight'] = $imageSize[1];
            $fileInfo['imageType'] = $ext;
        }

        // 上传
        $bucket = $this->getDefaultBucket();
        $object = $this->getDefaultObject($ext, $objectPrefix);
        $ossClient = $this->getClientIns();
        $result = $ossClient->uploadFile($bucket, $object, $tmpSavePath);
        // 删除临时存储文件
        @unlink($tmpSavePath);
        if (!$result) {
            return false;
        }

        $result = $this->appendInfo($result);
        return $result;
    }

    protected function appendInfo($uploadResult)
    {
        $ossRequestUrl = $uploadResult['oss-request-url'];
        $urlInfo = parse_url($ossRequestUrl);
        $uploadResult['access'] = [
            'server' => $this->getDefaultServer() ?: $urlInfo['schema'] . '://' . $urlInfo['host'],
            'path' => $urlInfo['path'],
            'fullUrl' => $this->getDefaultServer() . $urlInfo['path'],
        ];

        return $uploadResult;
    }

    /**
     * 获取文件后缀名通过请求头
     * @param $fileUrl
     *
     * @return false|mixed|string
     * @author Chris Yu <chrisyu@crabapple.top> 2023/9/21
     */
    protected function getFileExtByFileUrlViaHeader($fileUrl)
    {
        $headers = get_headers($fileUrl, 1);
        // 从 HTTP 头信息中获取 MIME 类型
        if (isset($headers['Content-Type'])) {
            $fileMimeType = $headers['Content-Type'];

            // 判断文件类型
            if (strpos($fileMimeType, 'image/') !== false) {
                $explode = explode('/', $fileMimeType);
                return $explode[1];
            }
            elseif (strpos($fileMimeType, 'audio/') !== false) {
                $explode = explode('/', $fileMimeType);
                $match = [
                    'mpeg' => 'mp3',
                    'wav' => 'wav',
                ];
                return $match[$explode[1]];
            }
            else {
                Log::error('Content-Type 类型错误', ['location' => __METHOD__ . '[' . __LINE__ . ']', 'fileUrl' => $fileUrl, 'mimeType' => $fileMimeType]);
                return false;
            }
        }
        else {
            Log::error('Content-Type 字段不存在', ['location' => __METHOD__ . '[' . __LINE__ . ']', 'fileUrl' => $fileUrl]);
            return false;
        }
    }

    /**
     * 获取文件后缀名通过 url
     * @param $fileUrl
     *
     * @return false|mixed|string
     * @author Chris Yu <chrisyu@crabapple.top> 2023/9/21
     */
    public function getFileExtByFileUrlViaUrl($fileUrl)
    {
        $urlParts = parse_url($fileUrl);

        // 从 URL 中获取文件名
        $fileName = basename($urlParts['path']);

        // 使用 pathinfo 函数获取文件信息
        $fileInfo = pathinfo($fileName);

        // 从文件信息中获取文件的扩展名
        return $fileInfo['extension'];
    }
}
